package data;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class G5File {
   public static final int MAX_CHUNK_SIZE = 1024 * 512; // 512KB
   public static final String DEFAULT_CHUNK_DIR = "D:\\torrent\\";
   
   /**
    * split file
    * 
    * @param fPath : path of source file
    * @param chunkDirectory : directory of chunk stored
    * @param torrentFilePath: path of torrent source file          
    * @throws IOException
    * @throws NoSuchAlgorithmException
    */
   public static void split(String fPath, String chunkDirectory,
         String torrentFilePath) throws IOException,
         NoSuchAlgorithmException {
      // variable
      FileInputStream fileInput = null; // stream read from device
      FileOutputStream fileOutput = null; // stream write to device

      File f = null; // file object
      File chunkDir = null; 
      
      // create directory if not exists
      if (!chunkDir.exists()) {
     	 chunkDir.mkdirs();	    
      }

      byte[] buffer = null; // buffer will store data

      int fileSize = 0, c = 1, gotFileSize = 0;

      String subFileName = "", subFilePath = ""; // name of subString
      String content = "", hashCode = "";
      
      try {
         // read file
         f = new File(fPath);
         chunkDir = new File(chunkDirectory);
         
         fileInput = new FileInputStream(f);

         fileSize = (int) f.length(); // get file size

         buffer = new byte[MAX_CHUNK_SIZE];

         while (fileSize > 0) {
            gotFileSize = fileInput.read(buffer, 0, MAX_CHUNK_SIZE);

            fileSize = fileSize - gotFileSize;

            subFileName = f.getName() + ".part" + c + ".chunk";
            subFilePath = chunkDir.getPath() + "\\" + subFileName;
            c++;

            fileOutput = new FileOutputStream(new File(subFilePath));
            fileOutput.write(buffer);
            fileOutput.flush();

            // get hash code
            hashCode = getFileHashCode_SHA1(buffer);
            
            content += subFileName + ' ' + hashCode + System.getProperty("line.separator");
         }
      } finally {
         if (fileInput != null) {
            fileInput.close();
         }
         if (fileOutput != null) {
            fileOutput.close();
         }
         // write torrent File
         writeTorrentFile(torrentFilePath, content);
      }
   }
   
   /** Write content to torrent file
    * 
    * @param torrentFileName
    * @param torrentFilePath
    * @param content
    * @throws IOException
    */
   public static void writeTorrentFile(String torrentFilePath, String content)
         throws IOException {
      FileOutputStream outStream = null;
      try {
         outStream = new FileOutputStream(torrentFilePath);

         byte[] buff = content.getBytes();

         outStream.write(buff);
         outStream.flush();
      } finally {
         if (outStream != null) {
            outStream.close();
         }
      }
   }
   
   /** get hash code from byte[] of file
    * 
    * @param buffer
    * @return
    * @throws NoSuchAlgorithmException
    */
   public static String getFileHashCode_SHA1(byte[] buffer) throws NoSuchAlgorithmException {
      String hashCode = "";

      MessageDigest complete = MessageDigest.getInstance("SHA-1");
      
      complete.update(buffer);

      byte[] b = complete.digest();
      for (int i = 0; i < b.length; i++) {
         hashCode += Integer.toString((b[i] & 0xff) + 0x100, 16).substring(1);
      }
      
      return hashCode;
   }
   
   /** get hash code from byte[] of file
    * 
    * @param buffer
    * @return
    * @throws NoSuchAlgorithmException
    * @throws IOException 
    */
   public static String getFileHashCode_SHA1(File file) throws NoSuchAlgorithmException, IOException {
      byte[] buffer = fileToByte(file);
      
      return getFileHashCode_SHA1(buffer);
   }
   
   /**
    * Read file into to byte[]
    * 
    * @param file
    * @return byte[]
    * @throws IOException
    */
   public static byte[] fileToByte(File file) throws IOException {
	    byte[] buffer = null;
	    ByteArrayOutputStream ous = null;
	    InputStream ios = null;
	    try {
	    	buffer = new byte[4096];
	        ous = new ByteArrayOutputStream();
	        ios = new FileInputStream(file);
	        int read = 0;
	        while ( (read = ios.read(buffer)) != -1 ) {
	            ous.write(buffer, 0, read);
	        }
	    } finally { 
	        if (ios != null) {
	        	ios.close();
	        }
	    }
	    return ous.toByteArray();
   }
   
   /**
    * Object to byte array
    * @param obj
    * @return
    * @throws IOException
    */
   public static byte[] objToByte (Object obj) throws IOException {
      byte[] buffer = null;
      ByteArrayOutputStream byteOutStream = null;
      ObjectOutput out = null;
      
      try {
         byteOutStream = new ByteArrayOutputStream();
         out = new ObjectOutputStream(byteOutStream);
         
         out.writeObject(obj);
         out.flush();
         
         buffer = byteOutStream.toByteArray();
      } finally {
         if (byteOutStream != null) {
            byteOutStream.close();
         }
      }
      return buffer;
   } 
   
   /**
    * Byte array to Object
    * @param buffer
    * @return
    * @throws IOException
    * @throws ClassNotFoundException
    */
   public static Object byteArrayToObj (byte[] buffer) throws IOException, ClassNotFoundException {
      Object obj = null;
      ByteArrayInputStream byteInStream = null;
      ObjectInput in = null;
      
      try {
         byteInStream = new ByteArrayInputStream(buffer);
         in = new ObjectInputStream(byteInStream);
         
         obj = in.readObject();
      } finally {
         if (byteInStream != null) {
            byteInStream.close();
         }
      }
      return obj;
   }
   
   /**
    * Compare two String
    * @param checkSum1
    * @param checkSum2
    * @return
    */
   public static boolean compareString (String checkSum1, String checkSum2) {
	   return checkSum1.equals(checkSum2);
   }
}
